
package empty.dsa.arch

import empty.dsa.util.ADL.{EleTrace, ModuleTrace}
import empty.dsa.util.dsaParam.PEParam
import empty.dsa.util.dsaParam.alu_Param
import empty.dsa.util.EleType._

import scala.collection.mutable.ListBuffer

//需要传入ALU的数目，和ALU对应的参数
//在下面的模块中，需要把ALU的输入输出和PE端口的输入输出连接
//然后实现，当存在多个ALU时，根据cfg选择输出PE的输出
//如果存在多个ALU时，需要加入MUX选择输出，
//添加mux数量，例如输出数为5,5,5,5时，mux数为5，当输出数为5,4,3时mux数为4
class PE(peParam: PEParam, name: String) extends ModuleTrace(name) {
  this.typeStr = "PE"
  this.deviceStr = peParam.deviceName
  this.width = peParam.width

  val inNum_max = peParam.alu_Params.map(_.in_num).max
  val inPortList_max = (0 until inNum_max).map {
    i => {
      "in" + i.toString
    }
  }.toList
  this.inPorts = inPortList_max

  val outNum_max = peParam.alu_Params.map(_.out_num).max
  val outPortList_max = (0 until outNum_max).map {
    i => {
      "out" + i.toString
    }
  }.toList
  this.outPorts = outPortList_max

  //添加mux数量，例如输出数为5,5,5,5时，mux数为5，当输出数为5,4,3时mux数为4
  val mux_count = findSecondLargest(peParam.alu_Params.map(_.out_num)).getOrElse(-1)
//  println("mux_count: " + mux_count)


  //当alu_num数目大于1时，需要添加mux，选择连接到PE.out的输出
  if(peParam.alu_num > 1) {
    val inlist = ListBuffer[String]()
    for (i <- 0 until peParam.alu_num) {
      inlist += ("in" + i)
//      println("inlist :" + inlist)
    }
//    println("inlist :" + inlist)
    for(i <- 0 until mux_count) {
      val MUX = new EleTrace("MUXPE" + i.toString, TYPE_Multiplexer.id, inlist.toList, List("out0"), List(peParam.alu_num, width))
//      println("muxcount = :" + mux_count)
      addEle(MUX)
      //添加mux和输出之间的连接

      addConnect(("MUXPE" + i.toString, "out0"),("this", "out" + i.toString))

    }

  }

 
  for (i <- 0 until peParam.alu_num) {
    val alu_type = peParam.alu_Params(i).alu_type
    val aluname = peParam.alu_Params(i).alu_name
//    println("aluname : " + aluname)
    alu_type match {
      

      case "sha1" => {
        val inPortList = (0 until peParam.alu_Params(i).in_num).map {
          i => {
            "in" + i.toString
          }
        }.toList
        val outPortList = (0 until peParam.alu_Params(i).out_num).map {
          i => {
            "out" + i.toString
          }
        }.toList
        val aluIns = new EleTrace(aluname, TYPE_sha1.id, inPortList, outPortList, peParam.alu_Params(i).param) //LM: 获取输入的ALU类型
        addEle(aluIns)
        //ALU的输入连接到PE.in
        inPortList.indices.map{
          i => addConnect(("this" , "in" + i.toString),(aluname ,  "in" + i.toString))
        }
        //当只有一个alu时，ALU的输出直接连接到PE.out
        if(peParam.alu_num == 1) {
          outPortList.indices.map {
            i => addConnect((aluname, "out" + i.toString), ("this", "out" + i.toString))
          }
        }
        else {
          if(peParam.alu_Params(i).out_num > mux_count) {
            for(k <- 0 until mux_count) {
              addConnect((aluname, "out" + k.toString), ("MUXPE" + k.toString, "in" + i.toString))
            }
            for(l <- mux_count until peParam.alu_Params(i).out_num) {
              addConnect((aluname, "out" + l.toString), ("this", "out" + l.toString))
            }
          }
          else {
            outPortList.indices.map {
              j => addConnect((aluname, "out" + j.toString), ("MUXPE" + j.toString, "in" + i.toString))
            }
          }
        }
      }     
  
      //case _ => DontCare      // 应该和chisel没有关系，为什么使用chisel中的参数DontCare?
    }
  }



  //返回第二大的值，例如3,4,5则返回4,例如3,4,4则返回4
  def findSecondLargest(lst: List[Int]): Option[Int] = {
    val distinctList = lst.sorted
    if (distinctList.length < 2) {
      None
    } else {
      Some(distinctList(distinctList.length - 2))
    }
  }

}


object PE {
  def apply(peParam: PEParam, name: String): PE = {
    new PE(peParam, name)
  }
}






